<?php

require_once('common.php');
require_once('DatabaseDriver.class.php');

// Database Commands
define('CMD_INSERT', 1);
define('CMD_UPDATE', 2);
define('CMD_DELETE', 3);
// Database Modes
define('DB_DEBUG', 0);
define('DB_DEBUG_BLOCK', 1);
define('DB_NORMAL', 2);
/* In case driver is not specified in connection string set default */
define('DATABASE_DEFAULT_DRIVER', 'mysql');

class Database {
	var $db_link;
	var $driver;
	var $mode;

	function Database($connectionString = NULL, $database = NULL) {
    $this->mode = DB_NORMAL;
		if(!is_null($connectionString))
			$this->connect($connectionString, $database);
	}
	
	/* This connects and selects the database.
	The connection string has the format "driver|host[:port];user;password".
	For example: "mysql|localhost:3306;root;pwd" */
	function connect($connectionString, $database = NULL) {
		$params = explode("|", $connectionString);
		if(count($params) < 2) {
			$driverType = DATABASE_DEFAULT_DRIVER;
			$connecParams = $params[0];
		} else {
			$driverType = $params[0];
			$connecParams = $params[1];
		}
		$this->driver =& createDriver($driverType);
		$this->db_link = $this->driver->connect($connecParams);
		if(!$this->db_link)
			$this->handleError('Could not connect');
		if(!is_null($database)) {
			$this->selectDb($database);
		}
	}
	
	function selectDb($database) {
		if(!$this->driver->selectDb($database, $this->db_link))
			$this->handleError('Could not select database');
	}
	
	// Closing connection
	function disconnect() {
		$this->driver->close($this->db_link);
	}

	// Performing SQL query
	function &query($querySql) {
		if(!$this->checkBeforeQuery($querySql))
			return new DatabaseResult(0, $this);
		$result = $this->driver->query($this->db_link, $querySql);
		if(!$result)
			$this->handleError('Query failed');

		return new DatabaseResult($result, $this);
	}

    function execute($sql) {
		if(!$this->checkBeforeQuery($sql))
			return 0;
        $result = $this->driver->query($this->db_link, $sql);
		if(!$result)
			$this->handleError('Execute failed');
		return $this->driver->getAffectedRows($this->db_link);
    }
	
	function queryScalar($sql) {
		$result =& $this->query($sql);
		$result->setNumArray();
		$row = $result->nextRow();
		if($row)
			return $row[0];
		return NULL;
    }
	
	function lastInsertedId() {
		return $this->driver->getLastInsertId($this->db_link);
	}
	
	// Utils
	function formatValue($value, $type = SQLVAL_TEXT) {
		return $this->driver->formatValue($value, $type, $this->db_link);
	}
	
	function formatInt($value) {
		return $this->formatValue($value, SQLVAL_NUMBER);
	}

	// Debugging
	function checkBeforeQuery($sql) {
		if($this->mode == DB_DEBUG || $this->mode == DB_DEBUG_BLOCK) {
			echo "<hr>\n$sql\n<hr><br>";
			if($this->mode == DB_DEBUG_BLOCK)
				return false;
		}
		return true;
	}
	
	function setDebug($debug, $block = false) {
		if($debug) {
			if($block)
				$this->mode = DB_DEBUG_BLOCK;
			else
				$this->mode = DB_DEBUG;
		} else
			$this->mode = DB_NORMAL;
	}
	
	function getError() {
		return $this->driver->getError($this->db_link);
	}
	
	function handleError($str = '') {
		if(isEmpty($str))
			$str = 'DB Error';
		$str .= ' :';
		app_error($str . $this->getError());
	}
}

class DatabaseResult {
	var $db;
	var $result;
	var $isAssoc;
	var $valid;

	function DatabaseResult($result, &$db) {
		$this->db =& $db;
		$this->result = $result;
		$this->setAssocArray();
	}
	
	// Rows
	function nextRow() {
		if(! $this->isValid()) return NULL;
		if($this->isAssoc)
			return $this->db->driver->nextRowAssoc($this->result);
		else
			return $this->db->driver->nextRowNum($this->result);
	}
	function oneRow() {
		$row = $this->nextRow();
		$this->free();
		return $row;
	}
	function getNumRows() {
		if(! $this->isValid()) return 0;
		return $this->db->driver->getNumRows($this->result);
	}
	function hasRows() {
		return $this->getNumRows() > 0;
	}
	function setAssocArray() {
		$this->isAssoc = true;
	}
	function setNumArray() {
		$this->isAssoc = false;
	}
	
	// Collecting data in arrays
	function getRows() {
		$arr = array();
		while($row = $this->nextRow()) {
			$arr[] = $row;
		}
		$this->free();
		return $arr;
	}
	function getRowsAssoc($key,$value) {
		$arr = array();
		while($row = $this->nextRow()) {
			$arr[$row[$key]] = $row[$value];
		}
		$this->free();
		return $arr;
	}
	function getValuesOf($column) {
		$arr = array();
		while($row = $this->nextRow()) {
			$arr[] = $row[$column];
		}
		$this->free();
		return $arr;
	}
	
	// Free resultset
	function free() {
		if(! $this->isValid()) return;
		$this->db->driver->freeResult($this->result);
	}
	
	// State
	function isValid() {
		return $this->result;
	}
	
	// Fields
	function getNumFields() {
		if(! $this->isValid()) return 0;
		return $this->db->driver->getNumFields($this->result);		
	}
	
	function getField($index) {
		if(! $this->isValid()) return null;
		return $this->db->driver->getField($index, $this->result);
	}
}

class SQLUtil {
	/* Builds a comma delimited list of columns for use with SQL from an
	   array containing the column names. */
	function makeSQLValList(&$fields, $addQuotes = false, $doValues = true) {
		$quote = "";
		if ($addQuotes)
			$quote = "`";
		$valList = "";
		foreach ($fields as $field => $value) {
			// Build the columns
			if (strlen($valList) != 0)
				$valList .= ", ";
			if($doValues) {
				$valList .= $quote . $value . $quote;
			} else {
				$valList .= $quote . $field . $quote;
			}
		}
		return $valList;
	}
	
	function makeDelete($table, $where = '') {
		$sql = "DELETE FROM `" . $table . "`";
		if (! isEmpty($where)) {
			$sql .= " WHERE $where";
		}
		return $sql;
	}
	
	function makeInsert($table, $values) {
		$columns = self::makeSQLValList($values, true, false);
		$values  = self::makeSQLValList($values, false, true);
		$sql = "INSERT INTO `" . $table .
			   "` (" . $columns . ") VALUES (" . $values . ")";
		return $sql;
	}
	
	function makeUpdate($table, $values, $where = '') {
		if(empty($values))
			return;
		$sql = "";
		foreach ($values as $column => $value) {
			if (strlen($sql) == 0) {
				$sql = "`" . $column . "` = " . $value;
			} else {
				$sql .= ", `" . $column . "` = " . $value;
			}
		}
		$sql = "UPDATE `" . $table . "` SET " . $sql;
		if (! isEmpty($where)) {
			$sql .= " WHERE $where";
		}
		return $sql;
	}
	
	function makeSelect($fields, $tables, $where = '', $order = '', $limitNum = '', $limitCant = '') 
	{
		if(!is_string($fields))
			$fields = self::makeSQLValList($fields, false, true);
		if(!is_string($tables))
			$tables = self::makeSQLValList($tables, false, true);
		$sql = "SELECT ". $fields ." FROM ". $tables;
		if (! isEmpty($where)) {
			$sql .= " WHERE $where";
		}
		if (! empty($order)) {
			$sql .= " ORDER BY ". self::makeSQLValList($order, false, true);
		}
		
		if (! isEmpty($limitNum) && ! isEmpty($limitCant)) {
			$sql .= " LIMIT $limitNum, $limitCant";
		}
		return $sql;
	}
}

?>